/**
 * This file is Copyright © 2008 Software Craftsmen Limited. All Rights Reserved.
 */
package com.softwarecraftsmen.orogeny.actions;

import com.softwarecraftsmen.orogeny.buildEnvironments.BuildDateTime;
import com.softwarecraftsmen.orogeny.buildEnvironments.BuildEnvironment;
import com.softwarecraftsmen.orogeny.buildLogs.BuildLog;
import static com.softwarecraftsmen.orogeny.buildLogs.Verbosity.Summary;
import static com.softwarecraftsmen.orogeny.buildLogs.Verbosity.AllDetails;
import com.softwarecraftsmen.orogeny.execution.SuccessOrFailure;
import static com.softwarecraftsmen.orogeny.execution.SuccessOrFailure.Success;
import static com.softwarecraftsmen.orogeny.filing.AbsoluteDirectoriesAndFiles.absoluteDirectoriesAndFiles;
import com.softwarecraftsmen.orogeny.filing.AbsoluteDirectory;
import com.softwarecraftsmen.orogeny.filing.AbsoluteFile;
import com.softwarecraftsmen.orogeny.filing.AbsolutePath;
import com.softwarecraftsmen.orogeny.filing.AbsolutePaths;
import com.softwarecraftsmen.orogeny.filing.FileName;
import com.softwarecraftsmen.orogeny.filing.RelativeDirectory;
import com.softwarecraftsmen.orogeny.filing.findFileFilters.AbstractFindFilesFilter;
import com.softwarecraftsmen.orogeny.filing.findFileFilters.FindFilesFilter;
import org.jetbrains.annotations.NotNull;

import static java.lang.String.format;
import java.util.LinkedHashSet;
import static java.util.Locale.UK;
import java.util.Set;

public abstract class TouchAction extends AbstractAction
{
	private final AbsolutePaths touchFiles;
	private final FindFilesFilter filter;

	protected TouchAction(final @NotNull AbsolutePaths touchFiles, final @NotNull FindFilesFilter filter)
	{
		this.touchFiles = touchFiles;
		this.filter = filter;
	}

	@NotNull
	public String describeWhatWeDo()
	{
		return "touch";
	}

	@NotNull
	public SuccessOrFailure execute(final @NotNull BuildLog buildLog, final @NotNull BuildEnvironment buildEnvironment)
	{
		final BuildDateTime touchTime = touchTime(buildEnvironment);
		buildLog.writeMessage(Summary, format(UK, "About to touch files with time %1$s", touchTime));
		touchFiles.findFiles(new AbstractFindFilesFilter()
		{
			private final Set<AbsoluteDirectory> touchedDirectories = new LinkedHashSet<>();

			public boolean include(final @NotNull AbsoluteDirectory root, final @NotNull RelativeDirectory relativeDirectory, final @NotNull FileName fileName)
			{
				if (!filter.include(root, relativeDirectory, fileName))
				{
					return false;
				}
				final AbsoluteDirectory currentDirectory = relativeDirectory.makeAbsolute(root);
				if (touchedDirectories.contains(currentDirectory))
				{
					return false;
				}
				buildLog.writeMessage(AllDetails, format(UK, "Touching directory %1$s", currentDirectory));
				touchTime.touch(currentDirectory);
				touchedDirectories.add(currentDirectory);

				final AbsoluteFile currentFile = currentDirectory.file(fileName);
				buildLog.writeMessage(AllDetails, format(UK, "Touching file %1$s", currentFile));
				touchTime.touch(currentFile);

				return false;
			}
		});
		return Success;
	}

	@NotNull
	protected abstract BuildDateTime touchTime(final @NotNull BuildEnvironment buildEnvironment);

	@NotNull
	public static SpecifyFrom touch(final @NotNull AbsolutePath... touchFiles)
	{
		return touch(absoluteDirectoriesAndFiles(touchFiles));
	}

	@NotNull
	public static SpecifyFrom touch(final @NotNull AbsolutePaths... touchFiles)
	{
		return new SpecifyFrom(absoluteDirectoriesAndFiles(touchFiles));
	}

	public static final class SpecifyFrom
	{
		private final AbsolutePaths touchFiles;

		private SpecifyFrom(final AbsolutePaths touchFiles) {this.touchFiles = touchFiles;}

		@NotNull
		public SpecifyWhere where(final @NotNull FindFilesFilter filter)
		{
			return new SpecifyWhere(filter);
		}

		public final class SpecifyWhere
		{
			private final FindFilesFilter filter;

			private SpecifyWhere(final @NotNull FindFilesFilter filter) {this.filter = filter;}

			@NotNull
			public TouchAction with(final @NotNull BuildDateTime touchTime)
			{
				return new TouchAction(touchFiles, filter)
				{
					@NotNull
					protected BuildDateTime touchTime(final @NotNull BuildEnvironment buildEnvironment)
					{
						return touchTime;
					}
				};
			}

			@NotNull
			public TouchAction withBuildDateTime()
			{
				return new TouchAction(touchFiles, filter)
				{
					@NotNull
					protected BuildDateTime touchTime(final @NotNull BuildEnvironment buildEnvironment)
					{
						return buildEnvironment.currentBuildDateTime();
					}
				};
			}
		}
	}
}
